#include "application.h"
#include "mainwindow.h"
#include "editors/editormanager.h"
#include "editors/startupeditor.h"
#include "editors/projectsummaryeditor.h"
#include "editors/graphicsobjecteditor.h"
#include "editors/animationeditor.h"
#include "editors/textureeditor.h"
#include "document.h"
#include "widgets/objectview.h"
#include "physics/collisionshapesdocument.h"
#include "physics/physics.h"
#include <QFile>

#include "serialization/documentsaver.h"
#include "serialization/documentloader.h"

Application::Application(int &argc, char **argv) :
    QApplication(argc, argv)
{
}

Application &Application::app()
{
    return *dynamic_cast<Application*>(QCoreApplication::instance());
}

bool Application::initInstance()
{
    m_mainWindow = new MainWindow;
    m_mainWindow->show();

    m_editorManager = new EditorManager(this);

    m_editorManager->registerEditor("Startup", new StartupEditor(this));
    m_editorManager->registerEditor("ProjectSummary", new ProjectSummaryEditor(this));
    m_editorManager->registerEditor("GraphicsObjectEditor", new GraphicsObjectEditor(this));
    m_editorManager->registerEditor("AnimationEditor", new AnimationEditor(this));
    m_editorManager->registerEditor("TextureEditor", new TextureEditor(this));

    m_editorManager->setActiveEditor("Startup");

    m_collisionShapesDocument = new CollisionShapesDocument(this);

    m_physics.reset(new Physics(50.0f));

    return true;
}

void Application::exitInstance()
{
}

QPointer<MainWindow> Application::mainWindow() const
{
    return m_mainWindow;
}

QPointer<Document> Application::createDocument()
{
    QPointer<ObjectView> objView = m_mainWindow->objectView();
    Q_ASSERT(objView);

    m_document = new Document(this);

    connect(m_document, SIGNAL(objectCreated(QPointer<AbstractObject>)),
            objView, SLOT(onObjectCreated(QPointer<AbstractObject>)));
    connect(m_document, SIGNAL(objectDeleted(QPointer<AbstractObject>)),
            objView, SLOT(onObjectDeleted(QPointer<AbstractObject>)));
    connect(m_document, SIGNAL(modifiedStateChanged(bool)),
            m_mainWindow, SLOT(setWindowModified(bool)));
    connect(m_document, SIGNAL(modifiedStateChanged(bool)),
            m_mainWindow, SLOT(updateGUI()));
    connect(objView, SIGNAL(objectSelectionChanged(QPointer<AbstractObject>)),
            m_document, SLOT(setCurrentObject(QPointer<AbstractObject>)));
    connect(m_document, SIGNAL(currentObjectChanged(QPointer<AbstractObject>)),
                        m_mainWindow, SLOT(onCurrentObjectChanged(QPointer<AbstractObject>)));
    connect(m_document, SIGNAL(currentObjectChanged(QPointer<AbstractObject>)),
            m_mainWindow, SLOT(updateMenuCreateObject(QPointer<AbstractObject>)));
    connect(m_document, SIGNAL(currentFilenameChanged(QString)),
            m_mainWindow, SLOT(onCurrentFilenameChanged(QString)));

    m_document->setCurrentFileName("");
    m_editorManager->setActiveEditor("ProjectSummary");
    m_mainWindow->updateMenuCreateObject(QPointer<AbstractObject>());

    return m_document;
}

QPointer<Document> Application::loadDocument(const QString &fileName)
{
    QPointer<Document> doc = createDocument();

    DocumentLoader loader;
    loader.serialize(fileName, doc);

    m_document = doc;
    m_document->setCurrentFileName(fileName);

    m_document->setModified(false);

    return doc;
}

bool Application::saveDocument()
{
    Q_ASSERT(m_document);

    QFile file(m_document->currentFileName());

    DocumentSaver saver;
    saver.serialize(m_document->currentFileName(), m_document);

    m_document->setModified(false);

    return true;
}

bool Application::closeDocument()
{
    delete m_document;

    // TODO: This is workaround
    m_mainWindow->objectView()->clear();
    m_editorManager->setActiveEditor("Startup");
    m_mainWindow->updateMenuCreateObject(QPointer<AbstractObject>());

    emit documentClosed();

    return true;
}

QPointer<Document> Application::document() const
{
    return m_document;
}

QPointer<EditorManager> Application::editorManager() const
{
    return m_editorManager;
}

QPointer<CollisionShapesDocument> Application::collisionShapesDocument() const
{
    return m_collisionShapesDocument;
}

boost::shared_ptr<Physics> Application::physics() const
{
    Q_ASSERT(m_physics);
    return m_physics;
}
